﻿using Falafel.Controls.Dialogs;
using Falafel.Services;

namespace Falafel.LoginUI
{
    using System;
    using System.ComponentModel.DataAnnotations;
    using System.ServiceModel.DomainServices.Client.ApplicationServices;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;

    /// <summary>
    /// Form that presents the login fields and handles the login process.
    /// </summary>
    public partial class LoginForm
    {
        internal TextBox TextBoxUserName { get; private set; }
        private LoginRegistrationWindow _parentWindow;
        private readonly LoginInfo _loginInfo = new LoginInfo();

        /// <summary>
        /// Creates a new <see cref="LoginForm"/> instance.
        /// </summary>
        public LoginForm()
        {
            InitializeComponent();

            // Set the DataContext of this control to the
            // LoginInfo instance to allow for easy binding
            DataContext = _loginInfo;
        }

        /// <summary>
        /// Sets the parent window for the current <see cref="LoginForm"/>.
        /// </summary>
        /// <param name="window">The window to use as the parent.</param>
        public void SetParentWindow(LoginRegistrationWindow window)
        {
            _parentWindow = window;
        }

        /// <summary>
        /// Handles <see cref="DataForm.AutoGeneratingField"/> to provide the PasswordAccessor.
        /// </summary>
        private void LoginForm_AutoGeneratingField(object sender, DataFormAutoGeneratingFieldEventArgs e)
        {
            if (e.PropertyName == "Password")
            {
                PasswordBox passwordBox = (PasswordBox)e.Field.Content;
                _loginInfo.PasswordAccessor = () => passwordBox.Password;
            }
            else if (e.PropertyName == "UserName")
            {
                TextBoxUserName = (TextBox)e.Field.Content;
            }
        }

        /// <summary>
        /// Submits the <see cref="LoginOperation"/> to the server
        /// </summary>
        private void LoginButton_Click(object sender, EventArgs e)
        {
            // We need to force validation since we are not using the standard OK
            // button from the DataForm.  Without ensuring the form is valid, we
            // would get an exception invoking the operation if the entity is invalid.
            if (loginForm.ValidateItem())
            {
                _loginInfo.CurrentLoginOperation = WebContext.Current.Authentication.Login(_loginInfo.ToLoginParameters(), LoginOperation_Completed, null);
                _parentWindow.AddPendingOperation(_loginInfo.CurrentLoginOperation);
            }
        }

        /// <summary>
        /// Completion handler for a <see cref="LoginOperation"/>. If operation succeeds,
        /// it closes the window. If it has an error, we show an <see cref="ErrorWindow"/>
        /// and mark the error as handled. If it was not canceled, but login failed, it must
        /// have been because credentials were incorrect so we add a validation error to
        /// to notify the user.
        /// </summary>
        private void LoginOperation_Completed(LoginOperation loginOperation)
        {
            if (loginOperation.LoginSuccess)
            {
                _parentWindow.Close();
            }
            else if (loginOperation.HasError)
            {
                loginOperation.MarkErrorAsHandled();

                ExceptionDialog dlg = new ExceptionDialog(loginOperation.Error, "Could Not Login");
                dlg.Show();
            }
            else if (!loginOperation.IsCanceled)
            {
                _loginInfo.ValidationErrors.Add(new ValidationResult(ErrorResources.ErrorBadUserNameOrPassword, new string[] { "UserName", "Password" }));
            }
        }

        /// <summary>
        /// Switches to the registration form.
        /// </summary>
        private void RegisterNow_Click(object sender, RoutedEventArgs e)
        {
            _parentWindow.NavigateToRegistration();
        }

        /// <summary>
        /// If a login operation is in progress and is cancellable, cancel it.
        /// Otherwise, close the window.
        /// </summary>
        private void CancelButton_Click(object sender, EventArgs e)
        {
            if (_loginInfo.CurrentLoginOperation != null && _loginInfo.CurrentLoginOperation.CanCancel)
            {
                _loginInfo.CurrentLoginOperation.Cancel();
            }
            else
            {
                _parentWindow.Close();
            }
        }

        /// <summary>
        /// Maps Esc to the cancel button and Enter to the OK button.
        /// </summary>
        private void LoginForm_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Escape)
            {
                CancelButton_Click(sender, e);
            }
            else if (e.Key == Key.Enter && loginButton.IsEnabled)
            {
                LoginButton_Click(sender, e);
            }
        }
    }
}
